﻿//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation, 2008
//
// File: Utilities.cs
//
// Contents: Utilities used for database access.
//
//
// Created by: waits 2008-11-20
//
//----------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.IO;


namespace Microsoft.Reputation.Tests.FrontEndCommon
{
    public class DataConvertor
    {
        public delegate TValue ConversionDelegate<TValue>(string inStr);

        public Guid GetGUID(string str)
        {
            if (str.Equals("EmptyGUID"))
            {
                return Guid.Empty;
            }
            return Guid.NewGuid();
        }

        public string GetString(string str)
        {
            switch (str)
            {
                case "NullString":
                    return null;
                case "EmptyString":
                    return string.Empty;
                case "SpacesOnly":
                    return "        ";
                default:
                    return Convert.ToString(str);
            }
        }

        public int GetInteger(string str)
        {
            return Convert.ToInt32(str);
        }

        public uint GetUint(string str)
        {
            return Convert.ToUInt32(str);
        }

        public object GetObject(string str)
        {
            return (object)str;
        }

        public byte[] GetByteArray(string str)
        {
            return Convertor.ConvertStringToByteArray(str);
        }

        public uint ToUIntFromHex(string inStr)
        {
            return uint.Parse(inStr, NumberStyles.HexNumber);
        }

        public ulong ToULongFromHex(string inStr)
        {
            return ulong.Parse(inStr, NumberStyles.HexNumber);
        }

        public IList<int> ToIntArray(string inStr)
        {
            string[] rawItems = inStr.Split(',');
            if ((rawItems.Length == 1) && (String.IsNullOrEmpty(rawItems[0])))
            {
                return null;
            }

            List<int> res = new List<int>();
            foreach (string curItem in rawItems)
            {
                int curRes;
                if (int.TryParse(curItem, out curRes))
                {
                    res.Add(curRes);
                }
            }

            if (res.Count == 0)
            {
                return new int[0];
            }

            return res.ToArray();
        }

        public IList<string> ToStringArray(string inStr)
        {
            string[] rawRes = inStr.Split(',');
            if ((rawRes.Length == 1) && (String.IsNullOrEmpty(rawRes[0])))
            {
                return null;
            }

            List<string> res = new List<string>();
            foreach (string curItem in rawRes)
            {
                if (!String.IsNullOrEmpty(curItem))
                {
                    res.Add(curItem.Trim());
                }
            }

            if (res.Count == 0)
            {
                return new string[0];
            }

            return res;
        }

        public TValue ConvertTo<TValue>(ConversionDelegate<TValue> converter, string inStr, TValue defVal)
        {
            if (!String.IsNullOrEmpty(inStr) && (converter != null))
            {
                return converter(inStr);
            }

            return defVal;
        }

    }

    public static class Convertor
    {
        private static bool IsBasicType(Type objType)
        {
            if (objType == null)
            {
                return false;
            }

            IList<Type> basicTypes = new Type[] {
                typeof(bool),
                typeof(byte),
                typeof(sbyte),
                typeof(char),
                typeof(short),
                typeof(ushort),
                typeof(int),
                typeof(uint),
                typeof(long),
                typeof(ulong),
                typeof(float),
                typeof(double),
                typeof(decimal),
                typeof(string),
            };

            return basicTypes.Contains(objType);
        }

        public static string ConvertByteArrayToString(IList<byte> inArray)
        {
            if (inArray == null)
            {
                return "";
            }

            string res = "";
            for (int idx = 0; idx < inArray.Count; idx++)
            {
                res += String.Format("{0:X2}", inArray[idx]);
            }

            return res;
        }

        public static byte[] ConvertStringToByteArray(string inStr)
        {
            string filteredInput = inStr
                .Replace("0x", "")
                .Replace("0X", "")
                .Replace(",", "")
                .Replace(" ", "")
                .Replace("{", "")
                .Replace("}", "")
                .Replace("[", "")
                .Replace("]", "")
                .ToUpper();

            if ((filteredInput.Length % 2) != 0)
            {
                throw new FormatException("Cannot parse string - number of digits is not even.");
            }

            byte[] res = new byte[filteredInput.Length / 2];

            for (int idx = 0; idx < filteredInput.Length; idx += 2)
            {
                if (!Uri.IsHexDigit(filteredInput[idx]) || !Uri.IsHexDigit(filteredInput[idx + 1]))
                {
                    throw new FormatException("Invalid characters found in input string.");
                }

                res[idx / 2] = Byte.Parse(filteredInput.Substring(idx, 2), NumberStyles.AllowHexSpecifier);
            }

            return res;
        }

        public static string ConvertToBase64String(string str)
        {
            if (String.IsNullOrEmpty(str))  // Incase the input string is null or emtpy (either of them is a valid input), returning the string itself.
            {
                return str;
            }
            else
            {
                byte[] strAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(str);
                string base64str = System.Convert.ToBase64String(strAsBytes);
                return base64str;
            }
        }

        public static string ConvertToString(object inObj)
        {
            if (inObj == null)
            {
                return null;
            }

            if (inObj.GetType().Equals(typeof(string)))
            {
                return ((string)(inObj));
            }

            if (Convertor.IsBasicType(inObj.GetType()))
            {
                return Convert.ToString(inObj);
            }

            if (inObj.GetType().Equals(typeof(byte[])))
            {
                return Convertor.ConvertByteArrayToString((inObj as byte[]));
            }

            if (inObj.GetType().Equals(typeof(Guid)))
            {
                return Convert.ToString(inObj);
            }

            return inObj.ToString();
        }

        public static object ConvertFromString(Type convertType, string inStr)
        {
            if ((convertType == null) || (inStr == null))
            {
                return null;
            }

            if (convertType.Equals(typeof(string)))
            {
                return inStr;
            }

            if (convertType.Equals(typeof(byte[])))
            {
                return Convertor.ConvertStringToByteArray(inStr);
            }
            else if (Convertor.IsBasicType(convertType))
            {
                if (convertType.Equals(typeof(bool)))
                {
                    return Convert.ToBoolean(inStr);
                }
                else if (convertType.Equals(typeof(byte)))
                {
                    return Convert.ToByte(inStr);
                }
                else if (convertType.Equals(typeof(sbyte)))
                {
                    return Convert.ToSByte(inStr);
                }
                else if (convertType.Equals(typeof(char)))
                {
                    return Convert.ToChar(inStr);
                }
                else if (convertType.Equals(typeof(short)))
                {
                    return Convert.ToInt16(inStr);
                }
                else if (convertType.Equals(typeof(ushort)))
                {
                    return Convert.ToUInt16(inStr);
                }
                else if (convertType.Equals(typeof(int)))
                {
                    return Convert.ToInt32(inStr);
                }
                else if (convertType.Equals(typeof(uint)))
                {
                    return Convert.ToUInt32(inStr);
                }
                else if (convertType.Equals(typeof(long)))
                {
                    return Convert.ToInt64(inStr);
                }
                else if (convertType.Equals(typeof(ulong)))
                {
                    return Convert.ToUInt64(inStr);
                }
                else if (convertType.Equals(typeof(float)))
                {
                    return Convert.ToSingle(inStr);
                }
                else if (convertType.Equals(typeof(double)))
                {
                    return Convert.ToDouble(inStr);
                }
                else if (convertType.Equals(typeof(decimal)))
                {
                    return Convert.ToDecimal(inStr);
                }
            }
            else if (convertType.Equals(typeof(Guid)))
            {
                return new Guid(inStr.Trim());
            }

            return null;
        }
    }

    public class SqlDataConvertor
    {
        private delegate object ConvertMethod(string dbType, string inValue);


        private Dictionary<string, ConvertMethod> m_convertMethods;


        public SqlDataConvertor()
        {
            this.m_convertMethods = new Dictionary<string, ConvertMethod>();

            this.m_convertMethods.Add("v_binary", new ConvertMethod(ConvertBinary));
            this.m_convertMethods.Add("v_image", new ConvertMethod(ConvertBinary));
            this.m_convertMethods.Add("v_timestamp", new ConvertMethod(ConvertBinary));
            this.m_convertMethods.Add("v_varbinary", new ConvertMethod(ConvertBinary));

            this.m_convertMethods.Add("v_bit", new ConvertMethod(ConvertBinary));

            this.m_convertMethods.Add("v_tinyint", new ConvertMethod(ConvertInteger));
            this.m_convertMethods.Add("v_smallint", new ConvertMethod(ConvertInteger));
            this.m_convertMethods.Add("v_int", new ConvertMethod(ConvertInteger));
            this.m_convertMethods.Add("v_bigint", new ConvertMethod(ConvertInteger));

            this.m_convertMethods.Add("v_datetime", new ConvertMethod(ConvertDateTime));
            this.m_convertMethods.Add("v_smalldatetime", new ConvertMethod(ConvertDateTime));

            this.m_convertMethods.Add("v_float", new ConvertMethod(ConvertReal));
            this.m_convertMethods.Add("v_real", new ConvertMethod(ConvertReal));

            this.m_convertMethods.Add("v_uniqueidentifier", new ConvertMethod(ConvertUniqueId));

            this.m_convertMethods.Add("v_char", new ConvertMethod(ConvertString));
            this.m_convertMethods.Add("v_nchar", new ConvertMethod(ConvertString));
            this.m_convertMethods.Add("v_text", new ConvertMethod(ConvertString));
            this.m_convertMethods.Add("v_ntext", new ConvertMethod(ConvertString));
            this.m_convertMethods.Add("v_nvarchar", new ConvertMethod(ConvertString));
            this.m_convertMethods.Add("v_varchar", new ConvertMethod(ConvertString));

        }


        public object Convert(string dbType, string inValue)
        {
            if (this.ConvertMethods.ContainsKey(dbType))
            {
                return this.ConvertMethods[dbType](dbType, inValue);
            }

            throw new FormatException("Unknown format (" + dbType + ") - cannot convert value: " + inValue);
        }


        private Dictionary<string, ConvertMethod> ConvertMethods
        {
            get
            {
                return this.m_convertMethods;
            }
        }


        private object ConvertBinary(string dbType, string inValue)
        {
            if ((inValue.Length % 2) != 0)
            {
                throw new FormatException("Binary string must have even number of characters.");
            }

            switch (dbType)
            {
                case "v_timestamp":
                    Int64 tsRes = Int64.Parse(inValue, NumberStyles.HexNumber);
                    return tsRes;

                case "v_binary":
                case "v_image":
                case "v_varbinary":
                    byte[] baRes = new byte[inValue.Length / 2];

                    for (int strIdx = 0, resIdx = 0; strIdx < inValue.Length; strIdx += 2, resIdx++)
                    {
                        baRes[resIdx] = Byte.Parse(inValue.Substring(strIdx, 2), NumberStyles.HexNumber);
                    }

                    return baRes;

                default:
                    throw new FormatException("Unknown format (" + dbType + ") - cannot convert value: " + inValue);
            }
        }

        private object ConvertBoolean(string dbType, string inValue)
        {
            int bitValue = int.Parse(inValue);

            if (bitValue == 0)
            {
                return false;
            }
            else if (bitValue == 1)
            {
                return true;
            }

            throw new FormatException("Cannot convert bit value to boolean: " + inValue);
        }

        private object ConvertInteger(string dbType, string inValue)
        {
            switch (dbType)
            {
                case "v_tinyint":
                    byte bRes = Byte.Parse(inValue);
                    return bRes;

                case "v_smallint":
                    Int16 sRes = Int16.Parse(inValue);
                    return sRes;

                case "v_int":
                    Int32 iRes = Int32.Parse(inValue);
                    return iRes;

                case "v_bigint":
                    Int64 lRes = Int64.Parse(inValue);
                    return lRes;

                default:
                    throw new FormatException("Unknown format (" + dbType + ") - cannot convert value: " + inValue);
            }
        }

        private object ConvertDateTime(string dbType, string inValue)
        {
            DateTime res = DateTime.Parse(
                inValue,
                CultureInfo.CurrentCulture.DateTimeFormat,
                DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
            return res;
        }

        private object ConvertDecimal(string dbType, string inValue)
        {
            decimal res = Decimal.Parse(inValue);
            return res;
        }

        private object ConvertReal(string dbType, string inValue)
        {
            switch (dbType)
            {
                case "v_float":
                    double dRes = Double.Parse(inValue);
                    return dRes;

                case "v_real":
                    float fRes = float.Parse(inValue);
                    return fRes;

                default:
                    throw new FormatException("Unknown format (" + dbType + ") - cannot convert value: " + inValue);
            }
        }

        private object ConvertUniqueId(string dbType, string inValue)
        {
            Guid res = new Guid(inValue);
            return res;
        }

        private object ConvertString(string dbType, string inValue)
        {
            return inValue;
        }
    }


    public static class DbUtilities
    {
        public static long ConvertDbTsToLong(byte[] dbTs)
        {
            if (dbTs == null)
            {
                throw new ArgumentNullException("dbTs", "Cannot convert null DBTS value.");
            }

            if (dbTs.Length != 8)
            {
                throw new ArgumentException("DBTS value should be exactly 8 bytes.", "dbTs");
            }

            byte[] reversedDbTs = ((byte[])(dbTs.Clone()));
            Array.Reverse(reversedDbTs);

            long res = BitConverter.ToInt64(reversedDbTs, 0);
            return res;
        }

        public static V ContainsString<V>(IDictionary<string, V> dictObj, string compValue, StringComparison compFlags) where V : class
        {
            if (dictObj == null)
            {
                return null;
            }

            foreach (string curKey in dictObj.Keys)
            {
                if (curKey.Equals(compValue, compFlags))
                {
                    return dictObj[curKey];
                }
            }

            return null;
        }
    }
}